package npr;

import processing.core.*;
import java.awt.event.MouseEvent;


public class VirtualTrackball{

    private boolean over = false;
    private float xangle = 0.0f;
    private float yangle = 0.0f;
    private float initxangle, inityangle;
    private float radius; // radius of trackball
    private float xpos;  // horizontal position of trackball
    private float ypos;  // vertical position of trackball
    private PMatrix3D accum, rotx, roty;
    private static PApplet parent;

    public VirtualTrackball(int x, int y, int rad) {
	xpos = x;
	ypos = y;
	radius = rad;
	rotx= new PMatrix3D();
	roty= new PMatrix3D();
	accum= new PMatrix3D();
	parent.registerMouseEvent(this);
    }

  
    public VirtualTrackball(int x, int y, int rad, float xangle, float yangle) {
	xpos = x;
	ypos = y;
	radius = rad;
	rotx= new PMatrix3D();
	roty= new PMatrix3D();
	accum= new PMatrix3D();
	float siny= parent.sin(yangle);
	float cosy= parent.cos(yangle);
	rotx.set(1,0,0,0,0,cosy,siny,0,0,-siny,cosy,0,0,0,0,1);
	float sinx= parent.sin(xangle);
	float cosx= parent.cos(xangle);
	roty.set(cosx,0,-sinx,0,0,1,0,0,sinx,0,cosx,0,0,0,0,1);
	accum.preApply(rotx);
	accum.preApply(roty);
	initxangle= xangle;
	inityangle= yangle;
	parent.registerMouseEvent(this);
    }


    public static void setParent(PApplet applet){

	parent= applet;
    }
   
    public void mouseEvent(MouseEvent event) {
	
	switch (event.getID()) {
	case MouseEvent.MOUSE_PRESSED:
	    if(event.getButton()==MouseEvent.BUTTON1){
		pressed(); 
	    }
	    break;
	case MouseEvent.MOUSE_DRAGGED:
	    dragged();
	    break;
	case MouseEvent.MOUSE_RELEASED:
	    released();
	    break;
	default:
	    break;
	}
    }


    public void update() {
	parent.applyMatrix(accum.m00,accum.m01,accum.m02,accum.m03,accum.m10,accum.m11,accum.m12,accum.m13,accum.m20,accum.m21,accum.m22,accum.m23,accum.m30,accum.m31,accum.m32,accum.m33);
    }

    protected void pressed(){
    
	float x= parent.mouseX-xpos;
	float y= parent.mouseY-ypos;
      
	if(-radius <= x && x <= radius && -radius <= y && y <= radius) {
      
	    float xz= (parent.sqrt(parent.sq(radius)-parent.sq(x)));
	    initxangle = -(parent.atan2(x, xz));

	    float yz= (parent.sqrt(parent.sq(radius)-parent.sq(y)));
	    inityangle = (parent.atan2(y, yz));
      
	    over = true;
	}
    }

    protected void dragged(){

	if(over){
      
	    float x = parent.mouseX-xpos;
	    float y = parent.mouseY-ypos;

	    float restx = x%(radius);
	    float resty = y%(radius);
	    float quartersx = (x-restx)/radius;
	    float quartersy = (y-resty)/radius;

	    float xz = (parent.sqrt(parent.sq(radius)-parent.sq(restx)));
	    xangle = -(quartersx*parent.PI/2)-(parent.atan2(restx, xz));

	    float yz= (parent.sqrt(parent.sq(radius)-parent.sq(resty)));
	    yangle = (quartersy*parent.PI/2)+(parent.atan2(resty, yz));
      
      
	    float siny= parent.sin(yangle-inityangle);
	    float cosy= parent.cos(yangle-inityangle);
	    rotx.set(1,0,0,0,0,cosy,siny,0,0,-siny,cosy,0,0,0,0,1);
	    float sinx= parent.sin(xangle-initxangle);
	    float cosx= parent.cos(xangle-initxangle);
	    roty.set(cosx,0,-sinx,0,0,1,0,0,sinx,0,cosx,0,0,0,0,1);
      
	    initxangle= xangle;
	    inityangle= yangle;
	    accum.preApply(rotx);
	    accum.preApply(roty);
	}
      
    }

    public void released(){
	over = false;
    } 
  
    public PMatrix3D rotation(){
	return accum;
    }

}